/*
 *    Copyright (c) 2018-2025, sloth All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: sloth
 */
package cn.s1995.sloth.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.s1995.sloth.admin.api.dto.FileUploadConfigDTO;
import cn.s1995.sloth.admin.api.dto.ProcessFileDTO;
import cn.s1995.sloth.admin.api.dto.SysFileGroupDTO;
import cn.s1995.sloth.admin.api.entity.SysFile;
import cn.s1995.sloth.admin.api.entity.SysFileGroup;
import cn.s1995.sloth.admin.api.utils.VideoUtil;
import cn.s1995.sloth.admin.api.vo.FileUploadVO;
import cn.s1995.sloth.admin.mapper.SysFileGroupMapper;
import cn.s1995.sloth.admin.mapper.SysFileMapper;
import cn.s1995.sloth.admin.service.SysFileService;
import cn.s1995.sloth.bizs.service.common.CacheService;
import cn.s1995.sloth.common.core.constant.CommonConstants;
import cn.s1995.sloth.common.core.constant.enums.file.ImageType;
import cn.s1995.sloth.common.core.util.FileUtils;
import cn.s1995.sloth.common.core.util.R;
import cn.s1995.sloth.common.core.util.SpringContextHolder;
import cn.s1995.sloth.common.file.core.FileProperties;
import cn.s1995.sloth.common.file.core.FileTemplate;
import com.amazonaws.services.s3.model.S3Object;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 文件管理
 *
 * @author Luckly
 * @date 2019-06-18 17:18:42
 */
@Slf4j
@Service
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {
    /**
     * 原文件后缀
     */
    private static final String SUFFIX_ORIGIN = "_origin";
    /**
     * 缩略图后缀
     */
    private static final String SUFFIX_THUMBNAIL = "_thumb";
    public static final String SUFFIX_COMPRESS = "_compress";

    /**
     * 图片默认宽高
     */
    private static final int DEFAULT_WIDTH = 375;
    private static final int DEFAULT_HEIGHT = 750;

    /**
     * 视频帧参数值
     */
    public static final String DEFAULT_VIDEO_FRAME_PARAM_VALUE = "video/snapshot,t_%s,f_jpg,w_0,h_0";

    @Resource
    private FileTemplate fileTemplate;
    @Resource
    private SysFileGroupMapper fileGroupMapper;
    @Resource
    private FileProperties properties;

    @Resource
    private CacheService cacheService;

    @Value("${global.oss-file-url}")
    private String ossFileUrl;

    /**
     * 上传文件
     *
     * @param file                   文件流
     * @param dir                    文件夹
     * @param groupId                分组ID
     * @param type                   类型
     * @param isHandlePicCompression 是否处理图片压缩
     * @param isHandleVideoFrame     是否处理视频帧
     * @param videoFrameParamValue   视频帧参数值
     * @return
     */
    @Override
    @SneakyThrows
    public R<FileUploadVO> uploadFile(MultipartFile file, String dir, Long groupId, String type, Boolean isHandlePicCompression, Boolean isHandleVideoFrame, String videoFrameParamValue) {
        String prefixFileName = IdUtil.simpleUUID();
        String extName = FileUtil.extName(file.getOriginalFilename());
        String fileName = prefixFileName + StrUtil.DOT + extName;
        String originFileName = fileName;
        // 设置文件名
        fileName = StrUtil.isNotEmpty(dir) ? (dir + "/" + fileName) : fileName;

        String url = StrUtil.isNotEmpty(ossFileUrl) ? (ossFileUrl + fileName) : fileName;

        // 构建返回对象
        FileUploadVO vo = FileUploadVO.builder()
                .bucketName(properties.getBucketName())
                .fileName(fileName)
                .originFileName(originFileName)
                .url(url)
                .build();

        // 构建处理文件DTO
        ProcessFileDTO processFileDTO = ProcessFileDTO.builder()
                .type(type)
                .dir(dir)
                .fileName(fileName)
                .originFileName(originFileName)
                .fileExtName(extName)
                .filePrefixName(prefixFileName)
                .fileSize(file.getSize())
                .originalFilename(file.getOriginalFilename())
                .groupId(groupId)
                .contentType(file.getContentType())

                .isHandleVideoFrame(isHandleVideoFrame)
                .isHandlePicCompression(isHandlePicCompression)
                .videoFrameParamValue(videoFrameParamValue)

                .build();

        // 上传文件&保存文件日志
        uploadFileAndSaveLog(file, processFileDTO);

        // 上传图片处理
        if (FileUtils.isImage(extName)) {
            // 处理缩略图片
            processUploadImage(processFileDTO, vo, file);
        }
        // 上传视频处理
        if (FileUtils.isVideo(extName)) {
            // 视频帧文件名
            String videoFrameExtName = ImageType.jpg.name();
            String videoFrameFileName = fileName + StrUtil.DOT + videoFrameExtName;

            processFileDTO.setVideoFrameFileName(videoFrameFileName);
            processFileDTO.setVideoFrameExtName(videoFrameExtName);

            if (BooleanUtil.isTrue(isHandleVideoFrame)) {
                // 预先保存视频帧数据
                vo.setVideoFrameFileName(videoFrameFileName);
                vo.setVideoFrameUrl(StrUtil.isNotEmpty(ossFileUrl) ? (ossFileUrl + videoFrameFileName) : videoFrameFileName);
            }

            // 异步处理视频
            SpringContextHolder.getBean(SysFileService.class).processUploadVideo(processFileDTO);
        }

        return R.ok(vo);
    }

    /**
     * 上传文件&保存文件日志
     *
     * @param file
     * @param dto
     */
    private void uploadFileAndSaveLog(MultipartFile file, ProcessFileDTO dto) {
        try (InputStream inputStream = file.getInputStream()) {
            putObject(dto.getDir(), dto.getFileName(), inputStream, dto.getContentType());
            // 文件管理数据记录,收集管理追踪文件
            fileLog(dto);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 处理缩略图片
     *
     * @param dto
     * @param vo
     * @param file
     */
    public void processUploadImage(ProcessFileDTO dto, FileUploadVO vo, MultipartFile file) {
        // 获取图片配置
        FileUploadConfigDTO.Image config = cacheService.getFileUploadConfig().getImage();

        // 文件大小超过限制，压缩图片
        if (dto.getFileSize() > config.getLimit().doubleValue() * 1024 * 1024) {
            long start = System.currentTimeMillis();
            // 保存压缩图片
            saveCompressImage(dto, config, vo, file);
            log.info("处理压缩图片耗时: {}ms", System.currentTimeMillis() - start);
        }
    }

    @Override
    @Async
    @SneakyThrows
    public void processUploadVideo(ProcessFileDTO processFileDTO) {
        // 获取视频配置
        FileUploadConfigDTO.Video config = cacheService.getFileUploadConfig().getVideo();

        // 创建视频临时文件
        File downloadFile = File.createTempFile(processFileDTO.getFilePrefixName(), StrUtil.DOT + processFileDTO.getFileExtName());
        // 将视频帧网络资源下载到临时文件中
        URL url = this.getUrl(processFileDTO.getDir(), processFileDTO.getFileName());
        HttpUtil.downloadFileFromUrl(url.toString(), downloadFile);
        processFileDTO.setVideoUrl(url.toString());
        processFileDTO.setVideoDownloadFile(downloadFile);

        if (BooleanUtil.isTrue(processFileDTO.getIsHandleVideoFrame())) {
            long start = System.currentTimeMillis();
            // 截取视频帧
            // extractFrame(processFileDTO, config.getScreenshotTime());
            extractFrameForAliOss(processFileDTO, config.getScreenshotTime());
            log.info("截取视频帧耗时: {}ms", System.currentTimeMillis() - start);
        }

        // 文件大小超过限制，压缩视频
        if (processFileDTO.getFileSize() > config.getLimit().doubleValue() * 1024 * 1024) {
            long start = System.currentTimeMillis();
            // 压缩视频
            compressVideo(downloadFile, processFileDTO);
            log.info("压缩视频耗时: {}ms", System.currentTimeMillis() - start);
        }

        // 删除临时文件
        FileUtil.del(downloadFile);
    }

    /**
     * 截取视频帧
     *
     * @param processFileDTO
     * @param screenshotTime 截屏时间
     */
    @SneakyThrows
    @Override
    public void extractFrameForAliOss(ProcessFileDTO processFileDTO, Integer... screenshotTime) {
        FileInputStream inputStream = null;
        File tempFile = null;
        try {
            // 设置默认视频帧参数值
            if (StrUtil.isEmpty(processFileDTO.getVideoFrameParamValue())) {
                // 视频帧文件名
                String videoFrameExtName = ImageType.jpg.name();
                String videoFrameFileName = processFileDTO.getFileName() + StrUtil.DOT + videoFrameExtName;

                processFileDTO.setVideoFrameFileName(videoFrameFileName);
                processFileDTO.setVideoFrameExtName(videoFrameExtName);

                Integer scTime;
                if (ArrayUtil.isNotEmpty(screenshotTime)) {
                    scTime = screenshotTime[0];
                } else {
                    // 获取视频配置
                    FileUploadConfigDTO.Video config = cacheService.getFileUploadConfig().getVideo();
                    scTime = config.getScreenshotTime();
                }

                processFileDTO.setVideoFrameParamValue(String.format(DEFAULT_VIDEO_FRAME_PARAM_VALUE, scTime * 1000));
            }
            String vfUrl = processFileDTO.getVideoUrl() + "?x-oss-process=" + processFileDTO.getVideoFrameParamValue();

            // 将视频帧网络资源下载到临时文件中
            tempFile = HttpUtil.downloadFileFromUrl(vfUrl, FileUtil.createTempFile());

            inputStream = new FileInputStream(tempFile);

            // 将视频帧上传至OSS
            putObject(processFileDTO.getDir(), processFileDTO.getVideoFrameFileName(), inputStream, "image/" + processFileDTO.getVideoFrameExtName());
            // 文件管理数据记录,收集管理追踪文件
            fileLog(processFileDTO, processFileDTO.getVideoFrameFileName(), (long) inputStream.available());

        } catch (Exception e) {
            log.error("上传视频帧失败", e);
            throw e;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw e;
                }
            }
            // 删除临时文件
            FileUtil.del(tempFile);
        }
    }


    /**
     * 截取视频帧
     *
     * @param dto
     * @param screenshotTime 截取时间
     */
    @SneakyThrows
    @Override
    public void extractFrame(ProcessFileDTO dto, Integer... screenshotTime) {
        FileInputStream inputStream = null;
        File tempFile = null;
        try {
            tempFile = FileUtil.createTempFile();

            Integer scTime;
            if (ArrayUtil.isNotEmpty(screenshotTime)) {
                scTime = screenshotTime[0];
            } else {
                // 获取视频配置
                FileUploadConfigDTO.Video config = cacheService.getFileUploadConfig().getVideo();
                scTime = config.getScreenshotTime();
            }

            VideoUtil.captureFrameAtSecond(dto.getVideoDownloadFile(), tempFile, scTime);
            inputStream = new FileInputStream(tempFile);

            // 将视频帧上传至OSS
            putObject(dto.getDir(), dto.getVideoFrameFileName(), inputStream, "image/" + dto.getVideoFrameExtName());
            // 文件管理数据记录,收集管理追踪文件
            fileLog(dto, dto.getVideoFrameFileName(), (long) inputStream.available());

        } catch (Exception e) {
            log.error("上传视频帧失败", e);
            throw e;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw e;
                }
            }
            // 删除临时文件
            FileUtil.del(tempFile);
        }
    }

    @Override
    public URL getUrl(String dir, String fileName) {
        return fileTemplate.getUrl(properties.getBucketName(), dir, fileName);
    }

    /**
     * 压缩视频
     *
     * @param videoFile
     * @param dto
     */
    @SneakyThrows
    private void compressVideo(File videoFile, ProcessFileDTO dto) {
        File tempFile = null;
        FileInputStream inputStream = null;
        try {
            // 创建临时文件
            tempFile = File.createTempFile(dto.getFilePrefixName() + SUFFIX_COMPRESS, StrUtil.DOT + dto.getFileExtName());

            // 压缩
            VideoUtil.compressVideo(videoFile, tempFile);

            inputStream = new FileInputStream(tempFile);

            // 视频帧文件名
            String compressFileName = dto.getFilePrefixName() + SUFFIX_COMPRESS + StrUtil.DOT + dto.getFileExtName();

            // 将视频帧上传至OSS
            putObject(dto.getDir(), compressFileName, inputStream, "video/" + dto.getFileExtName());
            // 文件管理数据记录,收集管理追踪文件
            fileLog(dto, compressFileName, (long) inputStream.available());
        } catch (Exception e) {
            log.error("上传视频帧失败", e);
            throw e;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw e;
                }
            }
            // 删除临时文件
            FileUtil.del(tempFile);
        }
    }

    /**
     * 保存压缩图片
     *
     * @param dto    上传文件信息
     * @param config 图片压缩配置
     * @param vo     上传结果视图对象
     * @param file   待压缩文件
     */
    private void saveCompressImage(ProcessFileDTO dto, FileUploadConfigDTO.Image config, FileUploadVO vo, MultipartFile file) {
        int width = DEFAULT_WIDTH;
        int height = DEFAULT_HEIGHT;

        // 解析配置中的宽高
        try {
            String[] split = config.getSpec().split("\\*");
            if (ArrayUtil.isNotEmpty(split) && split.length == 2) {
                width = Integer.parseInt(split[0]);
                height = Integer.parseInt(split[1]);
            }
        } catch (NumberFormatException e) {
            log.error("Parsing width and height from config failed, using default values. Error: {}", e.getMessage());
        }

        String fileName = dto.getFilePrefixName() + SUFFIX_THUMBNAIL + StrUtil.DOT + dto.getFileExtName();
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            // 压缩图片并写入输出流
            Thumbnails
                    .of(file.getInputStream())
                    // .of(new ByteArrayInputStream(file.getBytes()))
                    .size(width, height)
                    // .scale(0.75)
                    .outputQuality(config.getQuality().doubleValue())
                    // .toFile("C:\\Users\\user\\Desktop\\test.jpg")
                    .toOutputStream(bos)
            ;

            try (ByteArrayInputStream resInputStream = new ByteArrayInputStream(bos.toByteArray())) {
                putObject(dto.getDir(), fileName, resInputStream, dto.getContentType());
                // 设置压缩图片名和URL
                vo.setThumbnailFileName(fileName);
                vo.setThumbnailUrl(StrUtil.isNotEmpty(ossFileUrl) ? (ossFileUrl + fileName) : fileName);
                // 文件管理数据记录
                fileLog(dto, fileName, (long) resInputStream.available());
            }
        } catch (Exception e) {
            log.error("保存压缩图片异常", e);
        }
    }

    /**
     * 上传文件
     *
     * @param dir
     * @param fileName
     * @param inputStream
     * @param contentType
     * @throws Exception
     */
    private void putObject(String dir, String fileName, InputStream inputStream, String contentType) throws Exception {
        fileTemplate.putObject(properties.getBucketName(), dir, fileName, inputStream, contentType);
    }

    /**
     * 读取文件
     *
     * @param fileName
     * @param response
     */
    @Override
    public void getFile(String fileName, HttpServletResponse response) {
        SysFile sysFile = baseMapper.selectOne(Wrappers.<SysFile>lambdaQuery().eq(SysFile::getFileName, fileName));

        // outFileURI(fileName, response, sysFile);

        getFileURI(fileName, response, sysFile);
    }

    /**
     * 输出文件资源
     *
     * @param fileName
     * @param response
     * @param sysFile
     */
    private void outFileURI(String fileName, HttpServletResponse response, SysFile sysFile) {
        try (S3Object s3Object = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName)) {
            handleFileType(response, sysFile);
            IoUtil.copy(s3Object.getObjectContent(), response.getOutputStream());
        } catch (Exception e) {
            log.error("文件读取异常: {}", e.getLocalizedMessage());
            response.setStatus(HttpStatus.HTTP_NOT_FOUND);
        }
    }

    /**
     * 获取文件资源
     *
     * @param fileName
     * @param response
     * @param sysFile
     */
    private void getFileURI(String fileName, HttpServletResponse response, SysFile sysFile) {
        handleFileType(response, sysFile);

        try {
            String dirPath = sysFile.getDir() + "/";
            // 去除文件夹路径
            if (fileName.contains(dirPath)) {
                fileName = fileName.replace(dirPath, "");
            }
            URL url = new URL(ossFileUrl + dirPath + fileName);
            IoUtil.copy(url.openStream(), response.getOutputStream());
        } catch (Exception e) {
            log.error("文件读取异常: {}", e.getLocalizedMessage());
            response.setStatus(HttpStatus.HTTP_NOT_FOUND);
        }
    }

    private static void handleFileType(HttpServletResponse response, SysFile sysFile) {
        if (sysFile.getType() != null && "20".equals(sysFile.getType())) {
            response.setContentType("video/*");
            // 获取文件大小
            long fileLength = sysFile.getFileSize();
            // 设置范围请求响应头
            String contentRange = "bytes 0-" + (fileLength - 1) + "/" + fileLength;
            response.setHeader("Content-Range", contentRange);
            // 设置缓存 1小时
            response.setHeader("Cache-Control", "public, max-age=3600");
            // 断点续传 状态码206
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        } else {
            response.setContentType("image/octet-stream; charset=UTF-8");
        }
    }


    /**
     * 删除文件
     *
     * @param id
     * @return
     */
    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteFile(Long id) {
        SysFile file = this.getById(id);
        if (Objects.isNull(file)) {
            return Boolean.FALSE;
        }
        fileTemplate.removeObject(properties.getBucketName(), file.getFileName());
        return this.removeById(id);
    }

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteFile(String fileName) {
        SysFile file = this.getOne(
                Wrappers.lambdaQuery(SysFile.class)
                        .eq(SysFile::getFileName, fileName),
                false
        );
        if (Objects.isNull(file)) {
            return Boolean.FALSE;
        }
        fileTemplate.removeObject(properties.getBucketName(), file.getFileName());
        return this.removeById(file.getId());
    }

    /**
     * 查询文件组列表
     *
     * @param fileGroup SysFileGroup对象，用于筛选条件
     * @return 包含文件组树形结构列表的List对象
     */
    @Override
    public List<Tree<Long>> listFileGroup(SysFileGroup fileGroup) {
        // 从数据库查询文件组列表
        List<TreeNode<Long>> treeNodeList = fileGroupMapper.selectList(Wrappers.query(fileGroup))
                .stream()
                .map(group -> {
                    TreeNode<Long> treeNode = new TreeNode<>();
                    treeNode.setName(group.getName());
                    treeNode.setId(group.getId());
                    treeNode.setParentId(group.getPid());
                    return treeNode;
                })
                .toList();

        // 构建树形结构
        List<Tree<Long>> treeList = TreeUtil.build(treeNodeList, CommonConstants.MENU_TREE_ROOT_ID);
        return CollUtil.isEmpty(treeList) ? new ArrayList<>() : treeList;
    }

    /**
     * 添加或更新文件组
     *
     * @param fileGroup SysFileGroup对象，要添加或更新的文件组信息
     * @return 添加或更新成功返回true，否则返回false
     */
    @Override
    public Boolean saveOrUpdateGroup(SysFileGroup fileGroup) {
        if (Objects.isNull(fileGroup.getId())) {
            // 插入文件组
            fileGroupMapper.insert(fileGroup);
        } else {
            // 更新文件组
            fileGroupMapper.updateById(fileGroup);
        }
        return Boolean.TRUE;
    }

    /**
     * 删除文件组
     *
     * @param id 待删除文件组的ID
     * @return 删除成功返回true，否则返回false
     */
    @Override
    public Boolean deleteGroup(Long id) {
        // 根据ID删除文件组
        fileGroupMapper.deleteById(id);
        return Boolean.TRUE;
    }

    /**
     * 移动文件组
     *
     * @param fileGroupDTO SysFileGroupDTO对象，要移动的文件组信息
     * @return 移动成功返回true，否则返回false
     */
    @Override
    public Boolean moveFileGroup(SysFileGroupDTO fileGroupDTO) {
        // 创建SysFile对象并设置groupId属性
        SysFile file = new SysFile();
        file.setGroupId(fileGroupDTO.getGroupId());

        // 根据IDS更新对应的SysFile记录
        baseMapper.update(file, Wrappers.<SysFile>lambdaQuery().in(SysFile::getId, fileGroupDTO.getIds()));
        return Boolean.TRUE;
    }

    /**
     * 文件管理数据记录，收集管理追踪文件
     *
     * @param dto
     */
    private void fileLog(ProcessFileDTO dto) {
        // 创建SysFile对象并设置相关属性
        SysFile sysFile = new SysFile();
        sysFile.setFileName(dto.getFileName());

        // 对原始文件名进行编码转换
        String originalFilename = new String(
                Objects.requireNonNull(dto.getOriginalFilename()).getBytes(StandardCharsets.ISO_8859_1),
                StandardCharsets.UTF_8);
        sysFile.setDir(dto.getDir());
        sysFile.setOriginal(originalFilename);
        sysFile.setFileSize(dto.getFileSize());
        sysFile.setBucketName(properties.getBucketName());
        sysFile.setType(dto.getType());
        sysFile.setGroupId(dto.getGroupId());

        // 调用save方法保存SysFile对象
        this.save(sysFile);
    }

    /**
     * 文件管理数据记录，收集管理追踪文件
     *
     * @param dto      AsyncProcessFileDTO对象
     * @param fileName 文件名
     * @param fileSize 文件大小
     */
    private void fileLog(ProcessFileDTO dto, String fileName, Long fileSize) {
        // 创建SysFile对象并设置相关属性
        SysFile sysFile = new SysFile();
        sysFile.setFileName(fileName);
        sysFile.setFileSize(fileSize);

        sysFile.setDir(dto.getDir());
        sysFile.setOriginal(dto.getFileName());
        sysFile.setType(dto.getType());
        sysFile.setGroupId(dto.getGroupId());

        sysFile.setBucketName(properties.getBucketName());

        // 调用save方法保存SysFile对象
        this.save(sysFile);

    }
}
